﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FirefliesClustering
{
    public class Firefly
    {
        Tuple<float, float>[] Value;
        float Function;
        float AttractivenessGamma;



        private static void EvaluateBounds(Tuple<float,float>[] Vectors, out float down, out float up, out float left, out float right)
        {
            left = Vectors[0].Item1;
            right = Vectors[0].Item1;
            down = Vectors[0].Item2;
            up = Vectors[0].Item2;
            foreach (var vector in Vectors)
            {
                if (vector.Item1 < left)
                {
                    left = vector.Item1;
                }
                if (vector.Item1 > right)
                {
                    right = vector.Item1;
                }
                if (vector.Item2 < down)
                {
                    down = vector.Item2;
                }
                if (vector.Item2 > up)
                {
                    up = vector.Item2;
                }
            }
        }

        private static float EuclideanDistance(Tuple<float, float> v, Tuple<float, float> u)
        {
            float value = (float) Math.Sqrt(Math.Pow(v.Item1 - u.Item1, 2) + Math.Pow(v.Item2 - u.Item2, 2));
            return value;
        }

        private static float CartesianDistance(Firefly X, Firefly Y)
        {
            float value = 0;
            for (int i = 0; i < X.Value.Length; i++)
            {
                value += (float) (Math.Pow(X.Value[i].Item1 - Y.Value[i].Item1, 2) + Math.Pow(X.Value[i].Item2 - Y.Value[i].Item2, 2));
            }
            value = (float)Math.Sqrt(value);
            return value;
        }

        private void MoveFireflyToAnother(Firefly MoreAttractive, Firefly BestFirefly)
        {
            float cartesian = CartesianDistance(this, MoreAttractive);
            float cartesianToBest = CartesianDistance(this, BestFirefly);
            float rand = (float)(new Random((int)DateTime.Now.Ticks).NextDouble());

            for (int i = 0; i < this.Value.Length; i++)
            {
                this.Value[i] = new Tuple<float, float>(this.Value[i].Item1 + (float)(Math.Pow(Math.E, (-1) * this.AttractivenessGamma *((float) (Math.Pow(cartesian, 2))))) * (MoreAttractive.Value[i].Item1 - this.Value[i].Item1)
                    + ((float)Math.Pow(Math.E, (-1) * this.AttractivenessGamma * ((float)(Math.Pow(cartesianToBest, 2))))) * (BestFirefly.Value[i].Item1 - this.Value[i].Item1) + rand,
                    this.Value[i].Item2 + (float)Math.Pow(Math.E, (-1) * this.AttractivenessGamma * ((float)Math.Pow(cartesian, 2))) * (MoreAttractive.Value[i].Item2 - this.Value[i].Item2)
                    + (float) Math.Pow(Math.E, (-1) * this.AttractivenessGamma * ((float)Math.Pow(cartesianToBest, 2))) * (BestFirefly.Value[i].Item1 - this.Value[i].Item2) + rand);
            }
        }
        
        public static Tuple<float, float>[] MakeCentroids(Tuple<float,float>[] Vectors, int K_Centroids, int F_Fireflies, int G_Generations, float A_Attractiveness)
        {
            
            //Вычислим границы для нашего рандома
                        
            float downBound;
            float upBound;
            float leftBound;
            float rightBound;
            EvaluateBounds(Vectors, out downBound, out upBound, out leftBound, out rightBound);
            
            //Определим начальные значения светлячков
            Firefly[] Fireflies = new Firefly[F_Fireflies];
            for (int i = 0; i < F_Fireflies; i++)
            {
                Fireflies[i] = new Firefly();
                Fireflies[i].Value = new Tuple<float, float>[K_Centroids];
                for (int j = 0; j < Fireflies[i].Value.Length; j++)
                {
                    Random random = new Random((int)DateTime.Now.Ticks + j);
                    //Random randomY = new Random((int)DateTime.Now.Ticks);
                    float rndmX = ((float)random.NextDouble()) * (rightBound - leftBound);
                    float rndmY = ((float)random.NextDouble()) * (upBound - downBound);
                    Fireflies[i].Value[j] = new Tuple<float, float>(leftBound + rndmX, downBound + rndmY);
                }
                Fireflies[i].AttractivenessGamma = A_Attractiveness;
            }

            // вычислим значения функции для светлячков
            foreach (Firefly ff in Fireflies)
            {
                float maxDistance = EuclideanDistance(new Tuple<float, float>(leftBound, downBound), new Tuple<float, float>(rightBound, upBound));
                float functionVal = 0;
                foreach (var vector in Vectors)
                {
                    float distance = maxDistance;
                    foreach (var centroid in ff.Value)
                    {
                        float distanceToCurrentCentroid = EuclideanDistance(vector, centroid);
                        if (distance > distanceToCurrentCentroid)
                        {
                            distance = distanceToCurrentCentroid;
                        }
                    }
                    functionVal += distance;
                }
                ff.Function = functionVal;
            }

            
            int currentGeneration = 0;
            Firefly currentBestFirefly = Fireflies[0];
            foreach (Firefly ff in Fireflies)
            {
                if (ff.Function < currentBestFirefly.Function)
                {
                    currentBestFirefly = ff;
                }
            }
            while (currentGeneration < G_Generations)
            {

                for (int i = 0; i < F_Fireflies; i++)
                {
                    for (int j = 0; j < F_Fireflies; j++){
                        // вычислим значения функции для светлячков
                        foreach (Firefly ff in Fireflies)
                        {
                            float maxDistance = EuclideanDistance(new Tuple<float, float>(leftBound, downBound), new Tuple<float, float>(rightBound, upBound));
                            float functionVal = 0;
                            foreach (var vector in Vectors)
                            {
                                float distance = maxDistance;
                                foreach (var centroid in ff.Value)
                                {
                                    float distanceToCurrentCentroid = EuclideanDistance(vector, centroid);
                                    if (distance > distanceToCurrentCentroid)
                                    {
                                        distance = distanceToCurrentCentroid;
                                    }
                                }
                                functionVal += distance;
                            }
                        }
                        if (Fireflies[i].Function < Fireflies[j].Function)
                        {
                            Fireflies[j].MoveFireflyToAnother(Fireflies[i], currentBestFirefly);
                        }
                    }
                }

                foreach (Firefly ff in Fireflies)
                {
                    if (ff.Function < currentBestFirefly.Function)
                    {
                        currentBestFirefly = ff;
                    }
                }
                currentGeneration++;
            }

            return currentBestFirefly.Value;
        }
   


        
    }
         
}
